package cn.dt.ymcc.service.impl;

import cn.dt.ymcc.GlobalException;
import cn.dt.ymcc.domain.Course;
import cn.dt.ymcc.domain.CourseMarket;
import cn.dt.ymcc.domain.KillActivity;
import cn.dt.ymcc.domain.KillCourse;
import cn.dt.ymcc.dto.KillPreOrderParamDto;
import cn.dt.ymcc.dto.Killpredto;
import cn.dt.ymcc.mapper.KillCourseMapper;
import cn.dt.ymcc.service.IKillActivityService;
import cn.dt.ymcc.service.IKillCourseService;
import cn.dt.ymcc.util.AssertUtil;
import cn.dt.ymcc.util.CodeGenerateUtils;
import cn.dt.ymcc.vo.CourseItemVo;
import cn.dt.ymcc.vo.OrderInfoVo;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.google.common.collect.Ordering;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Set;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author whale.dai
 * @since 2022-08-05
 */
@Service
public class KillCourseServiceImpl extends ServiceImpl<KillCourseMapper, KillCourse> implements IKillCourseService {
    @Autowired
    private IKillCourseService killCourseService;
    @Autowired
    private IKillActivityService killActivityService;
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;
    @Autowired
    private RedissonClient redissonClient;

    @Override
    public void save(KillCourse killCourse) {
        Date now = new Date();
        KillCourse killCourseTmp = selectByActivityIdAndCourseId(killCourse.getActivityId(),killCourse.getCourseId());
        AssertUtil.isNull(killCourseTmp,"秒杀课程已存在");
        KillActivity killActivity = killActivityService.selectById(killCourse.getActivityId());
        killCourse.setStartTime(killActivity.getStartTime());
        killCourse.setEndTime(killActivity.getEndTime());
        killCourse.setTimeStr(killActivity.getTimeStr());
        killCourse.setCreateTime(now);
        killCourseService.insert(killCourse);


    }

    /**
     * 查询所有上线的秒杀的课程
     * 从redis获取到所有的killCourse
     *
     * @return
     */
    @Override
    public List<KillCourse> online() {
        Set<Object> keys = redisTemplate.keys("activity:*");
        ArrayList<KillCourse> allcourses = new ArrayList<>();
        keys.forEach(key->{
            List killCourses = redisTemplate.opsForHash().values(key);
            allcourses.addAll(killCourses);
        });
        return allcourses;
    }


    /**
     * 查询秒杀课程是否已经存在
     * @param activityId
     * @param courseId
     * @return
     */
    private KillCourse selectByActivityIdAndCourseId(Long activityId, Long courseId) {
        Wrapper<KillCourse> wrapper=new EntityWrapper<>();
        wrapper.eq("course_id",courseId)
                .eq("activity_id",activityId);
        return killCourseService.selectOne(wrapper);
    }

    /**
     * 通过killId和activityId从redis获取KillCourse对象
     * @param activityId
     * @param killId
     * @return
     */
    @Override
    public KillCourse onlineOne(Long activityId, Long killId) {
        AssertUtil.isNotNull(activityId,"activityId参数不能为空");
        AssertUtil.isNotNull(killId,"killId参数不能为空");
        Object killcourseobj = redisTemplate.opsForHash().get("activity:" + activityId, killId.toString());
        KillCourse killCourse = (KillCourse) killcourseobj;
        return killCourse;
    }

    /**
     * 立即秒杀
     * 1.判断是否是在秒杀中
     * 2.扣减信号量
     * 3生成预售订单
     * 大key："activity:"+activityId,
     * 小key:killCoure.getId().toString()
     * @param dto 包含activityId，killCourseId
     * @return
     */
    @Override
    public String kill(Killpredto dto) {
        Long userId = 3L;
        Integer killCount = 1;
        Date now = new Date();
        AssertUtil.isNotNull(dto,"请求不合法");
        //从redis拿到存入的killCourse
        Object killCourseTmp = redisTemplate.opsForHash().get("activity:" + dto.getActivityId(), dto.getKillCourseId().toString());
        AssertUtil.isNotNull(killCourseTmp,"redis库中不存在你想要的，请检查一下哦");
        KillCourse killCourse = (KillCourse) killCourseTmp;
        //判断是否是在秒杀中
        boolean killing = now.after(killCourse.getStartTime()) && now.before(killCourse.getEndTime());
        AssertUtil.isTrue(killing,"商品不在秒杀中");
        String key = dto.getActivityId()+":"+dto.getKillCourseId();//这是用来获取redis中value的key
        //2.设置信号量,通过activityId获取对应t_kill_course的所有对象并存入redis中，
        //获取每一个课程对应的信号量
        RSemaphore semaphore = redissonClient.getSemaphore(key);
        //删减信号量
        boolean acquire = semaphore.tryAcquire(killCount);
        AssertUtil.isTrue(acquire,"当前人数众多，请再次尝试");
        //生成预售订单PreOrderParam
        String orderNo = CodeGenerateUtils.generateOrderSn(userId);
        KillPreOrderParamDto PreOrderParam = new KillPreOrderParamDto(orderNo, userId, killCourse);
        //存入redis用预售订单号当做key
        redisTemplate.opsForValue().set(orderNo,PreOrderParam);
        return orderNo;
    }

    /**
     * 秒杀订单的商品清单
     *从redis中拿到
     * @param orderNo
     * @return
     */
    @Override
    public OrderInfoVo onlineOneByOrder(String orderNo) {
        ArrayList<CourseItemVo> courseInfos = new ArrayList<>();
        Object PreOrderParamTmp = redisTemplate.opsForValue().get(orderNo);
        KillPreOrderParamDto preOrderParam = (KillPreOrderParamDto) PreOrderParamTmp;
        KillCourse killCourse = preOrderParam.getKillCourse();
        Course course = new Course();
        CourseMarket courseMarket = new CourseMarket();
        //设置course对象
        course.setId(killCourse.getId());
        course.setPic(killCourse.getCoursePic());
        course.setName(killCourse.getCourseName());
        //设置courseMarket对象
        courseMarket.setPrice(killCourse.getKillPrice());
        courseMarket.setPriceOld(new BigDecimal(50000));
        CourseItemVo courseItemVo = new CourseItemVo(course,courseMarket);
        boolean result = courseInfos.add(courseItemVo);
        AssertUtil.isTrue(result,"存入数据失败了，请查看原因");
        OrderInfoVo orderInfoVo = new OrderInfoVo(courseInfos,killCourse.getKillPrice());



        return orderInfoVo;
    }
}
